//
//  simple_timer.h
//
//  Created by Colin on 2019-08-05.
//  Copyright (c) 2019 Sumscope. All rights reserved.
//
#pragma once

#include <atomic>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>

#include "../core.h"

#ifdef WIN32
#include <windows.h>  // for ::Sleep() API
#endif

namespace qb {
class S_CORE_EXPORT simple_timer {
 public:
  simple_timer();
  simple_timer(const simple_timer& t);
  ~simple_timer();

  void start(int interval, std::function<void()> task);

  /*
   * this is a asynchronization function, only notify simple_timer to stop, but
   * not wait simple_timer thread exit.
   */
  void notify_stop();

  /*
   * this is a synchronization function, notify simple_timer to stop and wait
   * simple_timer thread finishing callback. so this function can't be called
   * in simple_timer callback function.
   */
  void stop();

  template <typename callable, class... arguments>
  void sync_wait(int after, callable&& f, arguments&&... args) {
    std::function<typename std::result_of<callable(arguments...)>::type()> task(
        std::bind(std::forward<callable>(f), std::forward<arguments>(args)...));
#ifdef WIN32
    ::Sleep(after);  // avoid sleep_until implementation bug.
#else
    std::this_thread::sleep_for(std::chrono::milliseconds(after));
#endif
    task();
  }

  template <typename callable, typename... arguments>
  void async_wait(int after, callable&& f, arguments&&... args) {
    std::function<typename std::result_of<callable(arguments...)>::type()> task(
        std::bind(std::forward<callable>(f), std::forward<arguments>(args)...));

    std::thread([after, task]() {
#ifdef WIN32
      ::Sleep(after);  // avoid sleep_until implementation bug.
#else
      std::this_thread::sleep_for(std::chrono::milliseconds(after));
#endif
      task();
    }).detach();
  }

 private:
  std::atomic<bool> m_expired;
  std::atomic<bool> m_try_to_expire;
  std::mutex m_mutex;
  std::condition_variable m_expired_cond;
};
}  // namespace qb